package multi_thread.master.lock_aqs;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.StampedLock;

/**
 * @author Cheng Jun
 * Description: StampedLock 比 ReentrantReadWriteLock 更高效，大概 5倍。
 * StampedLock 比 ReentrantReadWriteLock 增加了乐观读操作，读的时候，能写，提高了并发性。但是单价是 牺牲了锁的重入性
 * 在 tryOptimisticRead 和 validate 之间读取公共资源，防止被别的线程修改
 * 虽然，StampedLock 比 ReentrantReadWriteLock 性能高 4~5倍，但是 StampedLock的中断问题导致 CPU 爆满
 * 所以，如果不是在要求特别高的并发场景下，就不要使用 StampedLock。ReentrantReadWriteLock 其实就已经够用了。
 * 建议阅读：https://www.dandelioncloud.cn/article/details/1512964859644952578
 * @version 1.0
 * @date 2022/5/18 14:18
 * @see StampedLock
 * @see ReentrantReadWriteLockExample
 */
public class StampedLockExample {

    public static void main(String[] args) throws InterruptedException {
        StampedLock stampedLock = new StampedLock();
        testWriteLock(stampedLock);
        // testOptimisticReadLock(stampedLock);
    }


    static void testWriteLock(StampedLock stampedLock) throws InterruptedException {
        // 获取写锁的 stamp
        long writeLockStamp = stampedLock.writeLock();
        System.out.println(Thread.currentThread().getName() + " 当前线程获取写锁，writeLockStamp：" + writeLockStamp);
        TimeUnit.SECONDS.sleep(1);

        // stampedLock 都是不可重入锁
        // writeLockStamp = stampedLock.writeLock();
        // System.out.println(Thread.currentThread().getName() + " 当前线程获取写锁，writeLockStamp：" + writeLockStamp);

        // failNumber == 0 表示获取锁失败
        long failNumber = stampedLock.tryWriteLock();
        System.out.println(Thread.currentThread().getName() + " 当前线程 tryWriteLock() 获取写锁失败，都会返回 " + failNumber);
        // 解锁的时候需要传入 之前获取到的版本号
        stampedLock.unlockWrite(writeLockStamp);
    }

    static void testOptimisticReadLock(StampedLock stampedLock) throws InterruptedException {
        // a stamp, or zero if exclusively locked
        long optimisticReadStamp = stampedLock.tryOptimisticRead();
        new Thread(() -> {
            long writeStamp = stampedLock.writeLock();
            System.out.println(Thread.currentThread().getName() + " 获取写锁成功");
            // 可以使用.unlock(readStamp) 释放写锁，也可以直接使用 unlockWrite(writeStamp)
            // stampedLock.unlock(writeStamp);
            stampedLock.unlockWrite(writeStamp);
        }, "Write-Thread ").start();
        // 为了让 Write-Thread 能提前运行
        TimeUnit.SECONDS.sleep(2);
        // 没有被别的独占锁获取过，就是 true，反之就是false
        boolean validate = stampedLock.validate(optimisticReadStamp);
        System.out.println(validate ? "没有被别的独占锁获取过" : "存在被别的独占锁获取过");

        // 再来一次试试
        optimisticReadStamp = stampedLock.tryOptimisticRead();
        validate = stampedLock.validate(optimisticReadStamp);
        System.out.println(validate ? "没有被别的独占锁获取过" : "存在被别的独占锁获取过");
    }
}
